﻿#include "AudioFilter.h"

extern "C" {

#include <libavfilter/avfilter.h>
#include <libavfilter/buffersink.h>
#include <libavfilter/buffersrc.h>
#include <libavutil/opt.h>
#include <libavutil/samplefmt.h>
#include <libavcodec/avcodec.h>
}

#include <cstdio>
#include "FrameRenderer.h"

AudioFilter::AudioFilter()
    : filterGraph(nullptr), bufferSrcContext(nullptr), bufferSinkContext(nullptr)
{

}

AudioFilter::~AudioFilter()
{
    cleanup();
}

bool AudioFilter::initFilterGraph(AVRational inTimebase, int inSampleRate, AVSampleFormat inFmt, uint64_t inChannelLayout,
                                    int outSampleRate, AVSampleFormat outFmt, uint64_t outChannelLayout, const char* filter_descr)
{
    // const char* filter_descr = ;//,atempo=1.0";
    int ret = 0;

    const AVFilter* buffersrc = avfilter_get_by_name("abuffer");
    const AVFilter* buffersink = avfilter_get_by_name("abuffersink");
    AVFilterInOut* outputs = avfilter_inout_alloc();
    AVFilterInOut* inputs = avfilter_inout_alloc();

    filterGraph = avfilter_graph_alloc();
    if (!outputs || !inputs || !filterGraph) {
        ret = AVERROR(ENOMEM);
        goto end;
    }

    char args[128];
    snprintf(args, sizeof(args),
             "time_base=%d/%d:sample_rate=%d:sample_fmt=%s:channel_layout=0x%llx",
             inTimebase.num, inTimebase.den, inSampleRate,
             av_get_sample_fmt_name(inFmt),
             inChannelLayout);

    ret = avfilter_graph_create_filter(&bufferSrcContext, buffersrc, "in", args, nullptr, filterGraph);
    if (ret < 0) {
        goto end;
    }

    ret = avfilter_graph_create_filter(&bufferSinkContext, buffersink, "out", nullptr, nullptr, filterGraph);
    if (ret < 0) {
        goto end;
    }

    static const enum AVSampleFormat out_sample_fmts[] = { outFmt, AV_SAMPLE_FMT_NONE };
    ret = av_opt_set_int_list(bufferSinkContext, "sample_fmts", out_sample_fmts, AV_SAMPLE_FMT_NONE, AV_OPT_SEARCH_CHILDREN);
    if (ret < 0) {
        goto end;
    }

    static const int64_t out_channel_layouts[] = { (int64_t)(outChannelLayout), -1 };
    ret = av_opt_set_int_list(bufferSinkContext, "channel_layouts", out_channel_layouts, -1, AV_OPT_SEARCH_CHILDREN);
    if (ret < 0) {
        goto end;
    }

    static const int out_sample_rates[] = { outSampleRate, -1 };
    ret = av_opt_set_int_list(bufferSinkContext, "sample_rates", out_sample_rates, -1, AV_OPT_SEARCH_CHILDREN);
    if (ret < 0) {
        goto end;
    }

    outputs->name = av_strdup("in");
    outputs->filter_ctx = bufferSrcContext;
    outputs->pad_idx = 0;
    outputs->next = nullptr;

    inputs->name = av_strdup("out");
    inputs->filter_ctx = bufferSinkContext;
    inputs->pad_idx = 0;
    inputs->next = nullptr;

    if ((ret = avfilter_graph_parse_ptr(filterGraph, filter_descr, &inputs, &outputs, nullptr)) < 0)
        goto end;
    if ((ret = avfilter_graph_config(filterGraph, nullptr)) < 0)
        goto end;

end:
    avfilter_inout_free(&inputs);
    avfilter_inout_free(&outputs);
    if (ret < 0) {
        cleanup();
    }
    return ret >= 0;
}

void AudioFilter::cleanup()
{
    if (filterGraph) {
        avfilter_graph_free(&filterGraph);
        filterGraph = nullptr;
    }
    bufferSrcContext = nullptr;
    bufferSinkContext = nullptr;
}

AVFrame* AudioFilter::filter(AVFrame* frame)
{
    if (!filterGraph) {
        return nullptr;
    }

    int ret = av_buffersrc_add_frame(bufferSrcContext, frame);
    if (ret < 0) {
        return nullptr;
    }

    AVFrame* filteredFrame = av_frame_alloc();
    ret = av_buffersink_get_frame(bufferSinkContext, filteredFrame);
    if (ret < 0) {
        av_frame_free(&filteredFrame);

        if(ret != AVERROR(EAGAIN))
            printf("Failed to get frame from buffersink, errCode: %d", ret);

        return nullptr;
    }

    return filteredFrame;
}
